
#ifndef UM_ALT_STRING_H
#define UM_ALT_STRING_H

#include <string>
#include <memory>
#include <atlstr.h>

#ifndef _ATL_CSTRING_NO_CRT
#define __UM_STRING_ATL_IMPL ATL::CStringT<T, StrTraitATL<T, ChTraitsCRT<T> > >
#else
#define __UM_STRING_ATL_IMPL ATL::CStringT<T, StrTraitATL<T> >
#endif


namespace UM {

template <class T, class _Traits, class _Alloc>
class basic_string {

	typedef __UM_STRING_ATL_IMPL _Atl_string;
	typedef basic_string<T, _Traits, _Alloc> _self_type;

public:

	typedef typename _Alloc::template rebind<T>::other allocator_type;

	typedef typename allocator_type::size_type size_type;
	typedef typename allocator_type::difference_type difference_type;
	typedef typename allocator_type::pointer pointer;
	typedef typename allocator_type::const_pointer const_pointer;
	typedef typename allocator_type::reference reference;
	typedef typename allocator_type::const_reference const_reference;
	typedef typename allocator_type::value_type value_type;

	typedef pointer iterator;
	typedef const_pointer const_iterator;

	static const size_type npos = -1;

public:

	basic_string(const value_type* p, size_type count, const allocator_type& a = allocator_type())
		: impl(p, count)
	{
	}

	basic_string(const value_type* p, const allocator_type& a = allocator_type())
		: impl(p)
	{
	}

	basic_string(const basic_string& other, 
				 size_type offset = 0, 
				 size_type count = npos, 
				 const allocator_type& a = allocator_type())
	{
		if (offset == 0 && (count == npos || count == other.length()))
		{
			this->impl = other.impl;
		}
		else
		{
			this->impl.SetString(other.c_str() + offset, static_cast<int>(count));
		}
	}

	basic_string(size_type count, value_type ch, const allocator_type& a = allocator_type())
		: impl(ch, static_cast<int>(count))
	{
	}

	explicit basic_string(const allocator_type& a = allocator_type())
	{
	}

	const value_type* data( ) const
	{
		return this->impl.GetString();
	}

	const value_type *c_str( ) const
	{
		return this->impl.GetString();
	}

	size_type length() const
	{
		return this->impl.GetLength();
	}

	bool empty() const
	{
		return this->impl.IsEmpty();
	}

	size_type max_size() const
	{
		return static_cast<size_type>(-1);
	}

	size_type size() const
	{
		return length();
	}

	void push_back(value_type ch) {
		append(1, ch);
	}

	basic_string& append(const value_type* _Ptr)
	{
		this->impl.Append(_Ptr);
		return *this;
	}

	basic_string& append(const value_type* _Ptr, size_type count)
	{
		this->impl.Append(_Ptr, count);
		return *this;
	}

	basic_string& append(const basic_string& other, size_type offset, size_type count)
	{
		this->impl.Append(other.c_str() + offset, count);
		return *this;
	}

	basic_string& append(const basic_string& other)
	{
		this->impl.Append(other.c_str());
		return *this;
	}

	basic_string& append(size_type count, value_type ch)
	{
		_self_type tmp(count, ch);
		this->impl.Append(tmp.c_str());
		return *this;
	}

	template<class InputIterator>
	basic_string& append(InputIterator first, InputIterator last)
	{
		this->impl.Append(first, last - first);
		return *this;
	}

	basic_string& assign(const value_type* _Ptr)
	{
		this->impl.SetString(_Ptr);
		return *this;
	}

	basic_string& assign(const value_type* _Ptr, size_type count)
	{
		this->impl.SetString(_Ptr, count);
		return *this;
	}

	basic_string& assign(const basic_string& other, size_type offset, size_type count)
	{
		this->impl.SetString(other.c_str() + offset, count);
		return *this;
	}

	basic_string& assign(const basic_string& other)
	{
		this->impl = other.impl;
		return *this;
	}

	basic_string& assign(size_type count,  value_type ch)
	{
		_self_type tmp(count, ch);
		this->impl.SetString(tmp.c_str());
		return *this;
	}

	template<class InputIterator>
	basic_string& assign(InputIterator first, InputIterator last)
	{
		this->impl.SetString(first, last - first);
		return *this;
	}

	const_reference at(size_type offset) const
	{
		return this->impl.GetString()[offset];
	}

	reference at(size_type offset)
	{
		return this->impl.GetBuffer()[offset];
	}

	size_type capacity() const
	{
		return this->impl.GetAllocLength();
	}

	iterator begin() {
		return this->impl.GetBuffer();
	}

	const_iterator begin() const {
		return data();
	}

	iterator end() {
		return this->impl.GetBuffer(); + length();
	}

	const_iterator end() const {
		return data() + length();
	}

	void clear()
	{
		this->impl.Empty();
	}

	//size_type copy(value_type* _Ptr, size_type count, size_type offset = 0) const
	//{
	//	if (count > length() - offset) {
	//		count = length() - offset;
	//	}
	//	_Traits::copy(_Ptr, c_str() + offset, count);
	//	return count;
	//}


	int compare(const basic_string& src) const
	{
		return compare(0, length(), src, 0, src.length());
	}

	int compare(size_type offset, size_type count, const basic_string& src) const
	{
		return compare(offset, count, src, 0, src.length());
	}

	int compare(size_type offset1, size_type count1, const basic_string& src, size_type offset2, size_type count2) const
	{
		return compare(offset1, count1, src.c_str() + offset2, count2);
	}

	int compare(const value_type* _Ptr) const
	{
		return compare(0, length(), _Ptr);
	}

	int compare(size_type offset, size_type count, const value_type* _Ptr) const
	{
		return compare(offset, count, _Ptr, npos);
	}

	int compare(size_type offset, size_type count1, const value_type* _Ptr, size_type count2) const
	{
		return _Traits::compare(c_str() + offset, _Ptr, count1 < count2 ? count1 : count2);
	}

	iterator erase(iterator first, iterator last)
	{
		this->impl.Delete(first - data(), last - first);
		return *this;
	}

	iterator erase(iterator it)
	{
		this->impl.Delete(it - data(), 1);
		return *this;
	}

	basic_string& erase(size_type offset = 0, size_type count = npos)
	{
		this->impl.Delete(offset, count);
		return *this;
	}

	size_type find(value_type ch, size_type offset = 0) const
	{
		return this->impl.Find(ch, static_cast<int>(offset));
	}

	size_type find(const value_type* _Ptr, size_type offset = 0) const
	{
		return this->impl.Find(_Ptr, static_cast<int>(offset));
	}

	size_type find(const value_type* _Ptr, size_type offset, size_type count) const
	{
		size_type Mysize = length();

		if (count == 0 && offset <= Mysize)
			return (offset);

		size_type num;
		if (offset < Mysize && count <= (num = Mysize - offset))
		{
			const T *first, *last;
			for (num -= count - 1, last = data() + offset; (first = _Traits::find(last, num, *_Ptr)) != 0;
				num -= first - last + 1, last = first + 1)
				if (_Traits::compare(first, _Ptr, count) == 0)
			return (first - data());
		}

		return npos;
	}

	size_type find(const basic_string& other, size_type offset = 0) const
	{
		return find(other.c_str(), offset);
	}

	size_type find_first_not_of(value_type ch, size_type offset = 0) const
	{
		return find_first_not_of(&ch, offset, 1);
	}

	size_type find_first_not_of(const value_type* _Ptr, size_type offset = 0) const
	{
		return find_first_not_of(_Ptr, offset, _Traits::length(_Ptr));
	}

	size_type find_first_not_of(const value_type* _Ptr, size_type offset, size_type count) const
	{
		if (offset < length())
		{
			const T *const last = data() + length();
			for (const T *first = data() + offset; first < last; ++first)
			{
				if (_Traits::find(_Ptr, count, *first) == 0)
					return (first - data());
			}
		}
		return npos;
	}

	size_type find_first_not_of(const basic_string& other, size_type offset = 0) const
	{
		return find_first_not_of(other.c_str(), offset);
	}

	size_type find_first_of(value_type ch, size_type offset = 0) const
	{
		return find_first_of(&ch, offset, 1);
	}

	size_type find_first_of(const value_type* _Ptr, size_type offset = 0) const
	{
		return find_first_of(_Ptr, offset, _Traits::length(_Ptr));
	}

	size_type find_first_of(const value_type* _Ptr, size_type offset, size_type count) const
	{
		if (0 < count && offset < length())
		{
			const T *const last = data() + length();
			for (const T *first = data() + offset; first < last; ++first)
			{
				if (_Traits::find(_Ptr, count, *first) != 0)
					return (first - data());
			}
		}

		return npos;
	}

	size_type find_first_of(const basic_string& other, size_type offset = 0) const
	{
		return find_first_of(other.c_str(), offset);
	}

	size_type find_last_not_of(value_type ch, size_type offset = npos) const
	{
		return find_last_not_of(&ch, offset, 1);
	}

	size_type find_last_not_of(const value_type* _Ptr, size_type offset = npos) const
	{
		return find_last_not_of(_Ptr, offset, _Traits::length(_Ptr));
	}

	size_type find_last_not_of(const value_type* _Ptr, size_type offset, size_type count) const
	{
		size_type Mysize = length();
		if (0 < Mysize)
		{
			for (const T *ptr = data() + (offset < Mysize ? offset : Mysize - 1); ; --ptr)
			{
				if (_Traits::find(_Ptr, count, *ptr) == 0)
					return (ptr - data());
				else if (ptr == data())
					break;
			}
		}
		return npos;
	}

	size_type find_last_not_of(const basic_string& other, size_type offset = npos) const
	{
		return find_last_not_of(other.c_str(), offset);
	}

	size_type find_last_of(value_type ch, size_type offset = npos) const
	{
		return find_last_of(&ch, offset, 1);
	}

	size_type find_last_of(const value_type* _Ptr, size_type offset = npos) const
	{
		return find_last_of(_Ptr, offset, _Traits::length(_Ptr));
	}

	size_type find_last_of(const value_type* _Ptr, size_type offset, size_type count) const
	{
		size_type Mysize = length();
		if (0 < count && 0 < Mysize)
		{
			for (const T *ptr = data() + (offset < Mysize ? offset : Mysize - 1); ; --ptr)
			{
				if (_Traits::find(_Ptr, count, *ptr) != 0)
					return (ptr - data());
				else if (ptr == data())
					break;
			}
		}
		return npos;
	}

	size_type find_last_of(const basic_string& other, size_type offset = npos) const
	{
		return find_last_of(other.c_str(), offset);
	}

	basic_string& insert(size_type pos, const value_type* _Ptr)
	{
		return insert(pos, _Ptr, _Traits::length(_Ptr));
	}

	basic_string& insert(size_type pos, const value_type* _Ptr, size_type count)
	{
		if (pos > length())
		{
			pos = length();
		}

		if (count == npos)
		{
			count = _Traits::length(_Ptr);
		}

		size_type newLen = length();
		if (count > 0)
		{
			newLen += count;

			pointer ptr = this->impl.GetBuffer(static_cast<int>(newLen));
			_Traits::move(ptr + pos + count, ptr + pos, length() - pos);
			_Traits::move(ptr + pos, _Ptr, count);
			this->impl.ReleaseBufferSetLength(newLen);
		}

		return *this;
	}

	basic_string& insert(size_type pos, const basic_string& other)
	{
		return insert(pos, other, 0, npos);
	}

	basic_string& insert(size_type pos, const basic_string& other, size_type offset, size_type count)
	{
		return insert(pos, other.c_str() + offset, count);
	}

	basic_string& insert(size_type offset, size_type count, value_type ch)
	{
		return insert(offset, &ch, count);
	}

	void insert(iterator it, value_type ch = value_type())
	{
		insert(it, 1, ch);
	}

	void insert(iterator it, size_type count, value_type ch)
	{
		size_type pos = it - begin();

		size_type newLen = length();
		if (count > 0)
		{
			newLen += count;
			pointer ptr = this->impl.GetBuffer(static_cast<int>(newLen));
			_Traits::move(ptr + pos + count, ptr + pos, length() - pos);
			_Traits::assign(ptr + pos, count, ch);
			this->impl.ReleaseBufferSetLength(static_cast<int>(newLen));
		}
	}

	template<class InputIterator>
	void insert(iterator it, InputIterator first, InputIterator last)
	{
		insert(it - begin(), first, last - first);
	}

	basic_string& replace(size_type pos, size_type count, const value_type* _Ptr)
	{
		return replace(pos, count, _Ptr, _Traits::length(_Ptr));
	}

	basic_string& replace(size_type offset, size_type count, const value_type* _Ptr, size_type num)
	{
		int _Mysize = this->length();

		if (offset > _Mysize)
		{
			offset = _Mysize;
		}
		if (count == 0)
		{
			return *this;
		}

		int n = _Traits::length(_Ptr);
		if (num > n)
		{
			num = n;
		}

		size_type newLen = _Mysize + num - count;

		pointer ptr = this->impl.GetBuffer(static_cast<int>(newLen));
		_Traits::move(ptr + offset + num, ptr + offset + count, length() - offset - count);
		_Traits::move(ptr + offset, _Ptr, num);
		this->impl.ReleaseBufferSetLength(newLen);

		return *this;
	}

	basic_string& replace(size_type pos, size_type count, const basic_string& src)
	{
		return replace(pos, count, src, 0, src.length());
	}

	basic_string& replace(size_type pos, size_type count1, const basic_string& src, size_type offset, size_type count2)
	{
		return replace(pos, count1, src.c_str() + offset, count2);
	}

	basic_string& replace(size_type offset, size_type count, size_type num, value_type ch)
	{
		int _Mysize = this->length();

		if (offset > _Mysize || count == 0)
		{
			return *this;
		}

		size_type newLen = _Mysize + num - count;

		pointer ptr = this->impl.GetBuffer(static_cast<int>(newLen));
		_Traits::move(ptr + offset + num, ptr + offset + count, length() - offset - count);
		_Traits::assign(ptr + offset, num, ch);
		this->impl.ReleaseBufferSetLength(newLen);

		return *this;
	}

	basic_string& replace(iterator first, iterator last, const value_type* _Ptr)
	{
		return replace(first - begin(), last - first, _Ptr);
	}

	basic_string& replace(iterator first, iterator last, const basic_string& src)
	{
		return replace(first - begin(), last - first, src);
	}

	basic_string& replace(iterator first, iterator last, const value_type* _Ptr, size_type count)
	{
		return replace(first - begin(), last - first, _Ptr, count);
	}

	basic_string& replace(iterator first, iterator last, size_type count, value_type ch)
	{
		return replace(first - begin(), last - first, count, ch);
	}

	template<class InputIterator>
	basic_string& replace(iterator first1, iterator last1, InputIterator first2, InputIterator last2)
	{
		return replace(first1, last1 - first1, first2, last2 - first2);
	}

	void reserve(size_type count = 0)
	{
		this->impl.Preallocate(static_cast<int>(count));
	}

	basic_string substr(size_type offset = 0, size_type count = npos) const
	{
		size_type _Mysize = length();
		if (count > _Mysize)
		{
			count = _Mysize;
		}
		basic_string ret;
		ret.impl = this->impl.Mid(static_cast<int>(offset), static_cast<int>(count));
		return ret;
	}

	basic_string& operator+=(value_type ch)
	{
		this->impl += ch;
		return *this;
	}

	basic_string& operator+=(const value_type* _Ptr)
	{
		this->impl += _Ptr;
		return *this;
	}

	basic_string& operator+=(const basic_string& right)
	{
		this->impl += right.impl;
		return *this;
	}

	basic_string& operator=(value_type ch)
	{
		this->impl = ch;
		return *this;
	}

	basic_string& operator=(const value_type* _Ptr)
	{
		this->impl = _Ptr;
		return *this;
	}

	basic_string& operator=(const basic_string& right)
	{
		this->impl = right.impl;
		return *this;
	}

	const_reference operator[](size_type offset) const
	{
		return this->impl.GetString()[offset];
	}

	reference operator[](size_type offset)
	{
		return this->impl.GetBuffer()[offset];
	}

private:
	_Atl_string impl;

};

template<class _Elem, class _Traits, class _Alloc>
inline basic_string<_Elem, _Traits, _Alloc> __CLRCALL_OR_CDECL operator+(
		const basic_string<_Elem, _Traits, _Alloc>& _Left,
		const basic_string<_Elem, _Traits, _Alloc>& _Right)
	{	// return string + string
	return (basic_string<_Elem, _Traits, _Alloc>(_Left) += _Right);
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	basic_string<_Elem, _Traits, _Alloc> __CLRCALL_OR_CDECL operator+(
		const _Elem *_Left,
		const basic_string<_Elem, _Traits, _Alloc>& _Right)
	{	// return NTCS + string
	return (basic_string<_Elem, _Traits, _Alloc>(_Left) += _Right);
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	basic_string<_Elem, _Traits, _Alloc> __CLRCALL_OR_CDECL operator+(
		const _Elem _Left,
		const basic_string<_Elem, _Traits, _Alloc>& _Right)
	{	// return character + string
	return (basic_string<_Elem, _Traits, _Alloc>(1, _Left) += _Right);
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	basic_string<_Elem, _Traits, _Alloc> __CLRCALL_OR_CDECL operator+(
		const basic_string<_Elem, _Traits, _Alloc>& _Left,
		const _Elem *_Right)
	{	// return string + NTCS
	return (basic_string<_Elem, _Traits, _Alloc>(_Left) += _Right);
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	basic_string<_Elem, _Traits, _Alloc> __CLRCALL_OR_CDECL operator+(
		const basic_string<_Elem, _Traits, _Alloc>& _Left,
		const _Elem _Right)
	{	// return string + character
	return (basic_string<_Elem, _Traits, _Alloc>(_Left) += _Right);
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	bool __CLRCALL_OR_CDECL operator==(
		const basic_string<_Elem, _Traits, _Alloc>& _Left,
		const basic_string<_Elem, _Traits, _Alloc>& _Right)
	{	// test for string equality
	return (_Left.compare(_Right) == 0);
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	bool __CLRCALL_OR_CDECL operator==(
		const _Elem * _Left,
		const basic_string<_Elem, _Traits, _Alloc>& _Right)
	{	// test for NTCS vs. string equality
	return (_Right.compare(_Left) == 0);
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	bool __CLRCALL_OR_CDECL operator==(
		const basic_string<_Elem, _Traits, _Alloc>& _Left,
		const _Elem *_Right)
	{	// test for string vs. NTCS equality
	return (_Left.compare(_Right) == 0);
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	bool __CLRCALL_OR_CDECL operator!=(
		const basic_string<_Elem, _Traits, _Alloc>& _Left,
		const basic_string<_Elem, _Traits, _Alloc>& _Right)
	{	// test for string inequality
	return (!(_Left == _Right));
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	bool __CLRCALL_OR_CDECL operator!=(
		const _Elem *_Left,
		const basic_string<_Elem, _Traits, _Alloc>& _Right)
	{	// test for NTCS vs. string inequality
	return (!(_Left == _Right));
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	bool __CLRCALL_OR_CDECL operator!=(
		const basic_string<_Elem, _Traits, _Alloc>& _Left,
		const _Elem *_Right)
	{	// test for string vs. NTCS inequality
	return (!(_Left == _Right));
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	bool __CLRCALL_OR_CDECL operator<(
		const basic_string<_Elem, _Traits, _Alloc>& _Left,
		const basic_string<_Elem, _Traits, _Alloc>& _Right)
	{	// test if string < string
	return (_Left.compare(_Right) < 0);
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	bool __CLRCALL_OR_CDECL operator<(
		const _Elem * _Left,
		const basic_string<_Elem, _Traits, _Alloc>& _Right)
	{	// test if NTCS < string
	return (_Right.compare(_Left) > 0);
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	bool __CLRCALL_OR_CDECL operator<(
		const basic_string<_Elem, _Traits, _Alloc>& _Left,
		const _Elem *_Right)
	{	// test if string < NTCS
	return (_Left.compare(_Right) < 0);
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	bool __CLRCALL_OR_CDECL operator>(
		const basic_string<_Elem, _Traits, _Alloc>& _Left,
		const basic_string<_Elem, _Traits, _Alloc>& _Right)
	{	// test if string > string
	return (_Right < _Left);
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	bool __CLRCALL_OR_CDECL operator>(
		const _Elem * _Left,
		const basic_string<_Elem, _Traits, _Alloc>& _Right)
	{	// test if NTCS > string
	return (_Right < _Left);
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	bool __CLRCALL_OR_CDECL operator>(
		const basic_string<_Elem, _Traits, _Alloc>& _Left,
		const _Elem *_Right)
	{	// test if string > NTCS
	return (_Right < _Left);
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	bool __CLRCALL_OR_CDECL operator<=(
		const basic_string<_Elem, _Traits, _Alloc>& _Left,
		const basic_string<_Elem, _Traits, _Alloc>& _Right)
	{	// test if string <= string
	return (!(_Right < _Left));
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	bool __CLRCALL_OR_CDECL operator<=(
		const _Elem * _Left,
		const basic_string<_Elem, _Traits, _Alloc>& _Right)
	{	// test if NTCS <= string
	return (!(_Right < _Left));
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	bool __CLRCALL_OR_CDECL operator<=(
		const basic_string<_Elem, _Traits, _Alloc>& _Left,
		const _Elem *_Right)
	{	// test if string <= NTCS
	return (!(_Right < _Left));
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	bool __CLRCALL_OR_CDECL operator>=(
		const basic_string<_Elem, _Traits, _Alloc>& _Left,
		const basic_string<_Elem, _Traits, _Alloc>& _Right)
	{	// test if string >= string
	return (!(_Left < _Right));
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	bool __CLRCALL_OR_CDECL operator>=(
		const _Elem * _Left,
		const basic_string<_Elem, _Traits, _Alloc>& _Right)
	{	// test if NTCS >= string
	return (!(_Left < _Right));
	}

template<class _Elem,
	class _Traits,
	class _Alloc> inline
	bool __CLRCALL_OR_CDECL operator>=(
		const basic_string<_Elem, _Traits, _Alloc>& _Left,
		const _Elem *_Right)
	{	// test if string >= NTCS
	return (!(_Left < _Right));
	}

typedef basic_string<char, std::char_traits<char>, std::allocator<char> > string;
typedef basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > wstring;

} // namespace UM

#endif // UM_ALT_STRING_H